import {assert} from "../../utils/assert";
import {stringUtils} from "../../utils/stringUtils";
import {Midway, MidwayConfig} from "../midway";
import {supportFunction} from "../../utils/supportFunction";
import {ModuleManager} from "../managers/ModuleManager";
import {EventBusManager} from "../managers/EventBusManager";
import {ScriptRender} from "../../utils/ScriptRender";
import {ModuleLoader} from "../loader/ModuleLoader";
import {ModuleProcessor, lifecycleMethod, controlMethod} from "../instance/ModuleProcessor";
import jQuery from 'jquery';
import {Workflow} from "../flow/Workflow";
import ModelLoader from "../loader/ModelLoader";
import {WekMapPolyFill} from "../../polyfill/WekMapPolyFill";

//poly fill
if(!window.WeakMap) {
    window.WeakMap = WekMapPolyFill;
}

const _bundleProcessor = new WeakMap();
const _bundleRunning = new WeakMap();
const _bundleEventBus = new WeakMap();

class Bundle {
    constructor(options) {
        if (!supportFunction.hasProp(Midway, 'hasReady')) {
            throw Error('Component is not initialized or initialization is not completed!')
        }

        assert(options === void 0, "init options can't null");
        assert(options.el === undefined, "init options must contain 'el' property");
        assert(options.launcher === undefined, "init options must contain 'launcher' property");
        const container = jQuery.find(options.el);
        assert(container.length === 0, "can not find the specified container element");

        this._containElement = container[0];
        this._launcher = options.launcher;
        this._model = {};

        if (options.model !== undefined) {
            this._model = options.model
        } else if (options.modelUrl !== undefined) {
            let key = this._launcher;
            //配置中新增Extend配置, 用于扩展默认配置
            if(options.extend && options.extend.moduleKey) {
                key = options.extend.moduleKey;
            }
            ModelLoader.loadModel(key, options.modelUrl).done(data => {
                this._model = data;
                if(options.extend.extraData && typeof options.extend.extraData === 'object') {
                    this._model['$extraData'] = options.extend.extraData
                }
            });
        }
        Object.defineProperty(this, '__key', {value: stringUtils.randomWord(false, 8)});
        _bundleProcessor.set(this, []);
        _bundleRunning.set(this, false);
        _bundleEventBus.set(this, undefined);
        //设置容器属性
        this._containElement.setAttribute("bundle", this.Key);
    }

    get Key() {
        return supportFunction.getOwn(this, '__key');
    }

    get Model() {
        return this._model;
    }

    get Processors() {
        return _bundleProcessor.get(this);
    }

    get EventBus() {
        return _bundleEventBus.get(this);
    }

    /**
     * 启动模块
     */
    start() {
        if (_bundleRunning.get(this) === true) {
            console.warn('This bundle is running');
            return;
        }
        _bundleRunning.set(this, true);
        loadModule(this._launcher).done((obj) => {
            if (obj.module) {
                //加载Bundle所需的样式和外部脚本库
                ModuleLoader.getInstance().load(obj.module).done(() => {
                    //创建事件总线对象
                    const eb = EventBusManager.getInstance().create(this);
                    _bundleEventBus.set(this, eb);
                    //创建启动模块
                    createProcessor.call(this, obj.module, {
                        code: obj.module.prototype.__code,
                        model: this.Model,
                        rendTo: this._containElement,
                        attributes: {style: {width: '100%', height: '100%'}}
                    });
                });
            }
        });
    }

    /**
     * 重置模块的模型
     */
    update(model) {
        const running = _bundleRunning.get(this);
        if(running === undefined) {
            console.warn('Bundle has been destroyed and can not execute the release method');
            return;
        }
        if (running === false) {
            console.warn('This bundle not running');
            return;
        }

        const launcher = this.Processors[0];
        if (launcher) {
            if(typeof model === 'string') {
                ModelLoader.loadModel(this._launcher, model).done(data => {
                    launcher.Model = data;
                });
            } else if (jQuery.isPlainObject(model)) {
                launcher.Model = data;
            } else {
                console.error('Invalid input parameter');
                return;
            }
            launcher.runLifecycle(lifecycleMethod.update).done(data => {
                if (data !== undefined) {
                    const dispatchModel = data.dispatch || false;
                    if (dispatchModel === true && data.adaptedModel !== undefined) {
                        for (let index = 1, len = this.Processors.length; index < len; index++) {
                            const processor = this.Processors[index];
                            if (data.adaptedModel[processor.ModuleCode] !== undefined) {
                                processor.Model = data.adaptedModel[processor.ModuleCode];
                                processor.runLifecycle(lifecycleMethod.update);
                            }
                        }
                    }
                }
            });
        }
    }

    /**
     * 释放模块
     */
    release() {
        const running = _bundleRunning.get(this);
        if(running === undefined) {
            console.warn('Bundle has been destroyed and can not execute the release method');
            return;
        }
        if (running === false) {
            console.warn('This bundle not running');
            return;
        }
        //释放事件总线
        this.EventBus.removeAllEventListener();
        EventBusManager.getInstance().remove(this.Key);
        _bundleEventBus.delete(this);
        //释放处理器对象
        const processors = _bundleProcessor.get(this);
        for (let key in processors) {
            processors[key].runLifecycle(lifecycleMethod.destroy);
        }
        _bundleProcessor.set(this, []);
        //释放Processor
        _bundleProcessor.delete(this);
        //删除Bundle的DOM
        if (this._containElement.firstChild) {
            this._containElement.firstChild.remove();
        }
        //删除Bundle运行标志
        _bundleRunning.delete(this);
    }
}

Object.defineProperty(Bundle.prototype, 'require', {
    value: function (options) {
        assert(typeof options.code !== 'string', 'The requested module code can not be empty');
        const def = jQuery.Deferred();
        loadModule(options.code).done((obj) => {
            assert(obj.module === undefined, "Unable to load the specified module:" + options.code);
            ModuleLoader.getInstance().load(obj.module).done(() => {
                const processor = createProcessor.call(this, obj.module, options);
                def.resolve(processor.ModuleInstance)
            });
        });
        return def.promise();
    }
});
Object.defineProperty(Bundle.prototype, 'getEventBus', {
    value: function () {
        return this.EventBus;
    }
});
Object.defineProperty(Bundle.prototype, 'registerFlow', {
    value: function () {
        if (arguments.length < 2) {
            console.warn("The number of parameters is incorrect");
            return;
        }
        const options = $.extend(arguments[1], {key: arguments[0]});
        Workflow.registerFlow.call(this, options);
    }
});
Object.defineProperty(Bundle.prototype, 'startFlow', {
    value: function () {
        Workflow.startFlow.apply(this, arguments);
    }
});

const bundlePromise = {};

function createBundlePromise(code) {
    if (!supportFunction.hasProp(bundlePromise, code)) {
        bundlePromise[code] = jQuery.Deferred();
    }
    return supportFunction.getOwn(bundlePromise, code);
}

function getBundlePromise(code) {
    if (!supportFunction.hasProp(code)) {
        return createBundlePromise(code);
    }
    return supportFunction.getOwn(bundlePromise, code);
}

function createProcessor(clazz, options) {
    const instance = new clazz();
    const {
        code = options.code,
        model = options.model || {},
        attributes = options.attributes || undefined
    } = options;
    const rendTo = (options.rendTo === undefined || options.rendTo === null) ? this._containElement : options.rendTo;
    let template = undefined;
    if (clazz.prototype.__template !== undefined) {
        template = clazz.prototype.__template.cloneNode(true);
    }
    let processor = new ModuleProcessor({
        code: code,
        template: template,
        style: clazz.prototype.__style,
        model: model,
        instance: instance,
        container: rendTo,
        context: this
    });
    this.Processors.push(processor);
    //执行模块生命周期流程
    processor.runInit();
    processor.runLifecycle(lifecycleMethod.render);
    //配置附加属性
    processor.render(attributes);
    runControl.call(this, controlMethod.BEFORE_RUN, processor);
    processor.runLifecycle(lifecycleMethod.run).done(() => {
        runControl.call(this, controlMethod.AFTER_RUN, processor);
        processor.runLifecycle(lifecycleMethod.show);
    });
    return processor;
}

function runControl(name, currentProcess) {
    const processors = this.Processors;
    processors.forEach(processor => {
        const currentKey = currentProcess.ProcessorKey, tempKey = processor.ProcessorKey;
        if (tempKey !== currentKey && processor.IsController) {
            if (!supportFunction.hasProp(processor.RunningModules, name)) {
                processor.RunningModules[name] = [];
            }
            if (processor.RunningModules[name].findIndex(item => {
                    return item === currentKey;
                }) === -1) {
                processor.RunningModules[name].push(currentKey);
                supportFunction.execute(processor.ModuleInstance, name, currentProcess.ModuleInstance);
            }
        }
    });
    if (currentProcess.IsController) {
        processors.forEach(processor => {
            const currentKey = currentProcess.ProcessorKey, tempKey = processor.ProcessorKey;
            if (tempKey !== currentKey) {
                if (!supportFunction.hasProp(currentProcess.RunningModules, name)) {
                    currentProcess.RunningModules[name] = [];
                }
                if (currentProcess.RunningModules[name].findIndex(item => {
                        return item === tempKey;
                    }) === -1) {
                    currentProcess.RunningModules[name].push(currentKey);
                    supportFunction.execute(currentProcess.ModuleInstance, name, processor.ModuleInstance);
                }
            }
        });
    }
}

function loadModule(code) {
    const bundleConfig = ModuleManager.getInstance().ModuleConfig;
    if (!supportFunction.hasProp(bundleConfig, code)) {
        throw Error('Can not find configuration information for specified Bundle!');
    }
    const config = bundleConfig[code];
    let promise = getBundlePromise(code);
    if (config.loaded === true) {
        return promise.promise();
    }
    if (ModuleManager.getInstance().exists(code)) {
        //如果已经加载过
        promise.resolve({module: ModuleManager.getInstance().get(code)});
    } else {
        //如果还没有加载过
        config.loaded = true;
        //加载指定URL的bundle
        ScriptRender.addJavascript(config.href)
            .done(() => {
                promise.resolve({module: ModuleManager.getInstance().get(code)});
            })
            .fail(() => {
                promise.reject();
                throw Error('Load Bundle error');
            });
    }
    return promise.promise();
}

export {Bundle};